package spoon.service;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import spoon.core.annotation.DomainInfo;
import spoon.core.domain.Game;
import spoon.core.exception.ConvertException;
import spoon.core.service.HttpService;
import spoon.dao.SbobetDao;

public abstract class SbobetAbstractService implements SbobetService {

	@Autowired
	protected HttpService httpService;

	@Autowired
	private SbobetDao sbobetDao;

	@Value("#{sbobet['site']}")
	protected String SITE;

	@Value("#{sbobet['url']}")
	protected String URL;

	protected String GATE_URL;

	protected String ITEM_CODE;

	protected String ITEM_NAME;

	protected Map<String, String> leagueMap;

	protected Map<String, String> teamMap;

	protected Logger log = LoggerFactory.getLogger(this.getClass());

	// ------------------------------------------
	@DomainInfo("국가정보")
	protected String DATA_NATION;

	@DomainInfo("리그정보")
	protected String DATA_LEAGUE;

	protected String SUB_TYPE;

	protected String BET_TYPE;

	protected static String PATTERN_LINK = "[1-9]{1},[0-9]+,\\[[0-9]+,'[^']+','[^']+','[A-Z0-9.]+',[0-9]+,'[^']+'";

	protected static String PATTERN_DATA = "\\[[0-9]+,(\\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[-0-9.]+,[0-9]+\\],\\[[0-9.,]+\\])";

	@Override
	public abstract List<Game> getLinkList();

	@Override
	public abstract void getGame(Game link);

	/**
	 * script를 setElement 단위로 잘라서 nation, league로 나눈다.
	 * 
	 * @param scritp
	 */
	protected void setDynamic(String link) {
		String script = httpService.getJavascript(URL + link);

		Pattern p = Pattern.compile("Element\\(([^;]+)\\);");
		Matcher m = p.matcher(script);

		while (m.find()) {
			String s = m.group(1);
			if (s.startsWith("'regions'")) {
				DATA_NATION = s;
			} else if (s.startsWith("'tournaments'")) {
				DATA_LEAGUE = s;
			}
		}
	}

	/**
	 * script를 setElement 단위로 잘라서 subType을 가져온다.
	 * 
	 * @param scritp
	 */
	protected void setStatic(String link) {
		String script = httpService.getJavascript(URL + link);

		Pattern p = Pattern.compile("Element\\('market\\-groups',\\[([^;]+)\\);");
		Matcher m = p.matcher(script);

		if (m.find()) {
			SUB_TYPE = m.group(1);
		}
	}

	/**
	 * script를 setElement 단위로 잘라서 betType을 가져온다.
	 * 
	 * @param scritp
	 */
	protected void setTemplate(String link) {
		String script = httpService.getJavascript(link);

		Pattern p = Pattern.compile("Element\\('market\\-displays',\\[([^;]+)\\);");
		Matcher m = p.matcher(script);

		if (m.find()) {
			BET_TYPE = m.group(1);
		}
	}

	/**
	 * 링크정보로 dynimic script 에서 정보를 가져온다.
	 * 
	 * @param gameLink
	 * @param link
	 */
	protected void getLinkInfo(Game link, String url) {

		String[] urls = url.split(",");
		String dynamicData = "";

		Pattern p = Pattern.compile("\\[(" + urls[1] + ",'[^']+',[0-9]+,[0-9]+)");
		Matcher m = p.matcher(DATA_LEAGUE);

		if (m.find()) {
			dynamicData = m.group(1);
		}

		String[] data = dynamicData.split(",");
		link.setGameId(urls[2].substring(1));
		link.setLeagueName(getLeague(urls[1]));
		link.setLeagueCode(getLeagueCode(link.getLeagueName()));
		link.setNation(getNation(data[3]));
		link.setHomeTeamName(getStringData(urls[3]));
		link.setHomeTeamCode(getTeamCode(link.getHomeTeamName()));
		link.setAwayTeamName(getStringData(urls[4]));
		link.setAwayTeamCode(getTeamCode(link.getAwayTeamName()));
		link.setGameDate(convertDate(getStringData(urls[7])));
		convertLink(link);
	}

	/**
	 * 문자열에서 ' 를 제외한 문자열만 가져온다.
	 * 
	 * @param league
	 * @return
	 */
	protected String getStringData(String data) {
		return data.substring(data.indexOf("'") + 1, data.lastIndexOf("'"));
	}
	
	/**
	 * 다음날을 가져오기 위한 링크
	 * 
	 * @param addDay
	 * @return
	 */
	protected String getDate(int addDay) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Calendar c = Calendar.getInstance();
		c.add(Calendar.DATE, addDay);
		return sdf.format(c.getTime());
	}

	/**
	 * 국가정보를 가져온다.
	 * 
	 * @param key
	 * @return
	 */
	protected String getNation(String key) {
		Pattern p = Pattern.compile("\\[" + key + ",'([^']+)'");
		Matcher m = p.matcher(DATA_NATION);
		String nation = "";

		if (m.find()) {
			nation = m.group(1);
		}

		return nation;
	}

	/**
	 * 리그 정보를 가져온다.
	 * 
	 * @param key
	 * @return
	 */
	protected String getLeague(String key) {
		Pattern p = Pattern.compile("\\[" + key + ",'([^']+)'");
		Matcher m = p.matcher(DATA_LEAGUE);
		String league = "";

		if (m.find()) {
			league = m.group(1);
		}

		return league;
	}

	/**
	 * 날짜 변환 표준시간으로
	 * 
	 * @param dt
	 * @return
	 */
	protected Date convertDate(String dt) {
		DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:ss");
		TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); // 중국시간 +8
		df.setTimeZone(tz);

		Date date = new Date();
		try {
			date = df.parse(dt);
		} catch (ParseException e) {
			throw new ConvertException(new Throwable("Sbobet - 날짜변경 에러 (" + dt + ")", e));
		}
		return date;
	}

	/**
	 * 링크정보를 넘겨줌 https://www.sbobet.com/리그/아이디/홈팀-vs-원정팀
	 * 
	 * @param gameLink
	 */
	protected void convertLink(Game game) {
		String link = GATE_URL + "/" + game.getLeagueName().toLowerCase().replace(" ", "-") + "/" + game.getGameId() + "/"
				+ game.getHomeTeamName().toLowerCase().replace(" ", "-") + "-vs-" + game.getAwayTeamName().toLowerCase().replace(" ", "-");
		game.setLink(link);
	}

	/**
	 * 핸디캡 변환
	 * 
	 * @param handicap
	 * @return
	 */
	protected Double convertHandicap(String handicap) {
		Double d = null;

		if (handicap == null || "".equals(handicap)) {
			d = 0D;
		} else {
			d = Double.parseDouble(handicap);
		}

		return d;
	}

	/**
	 * 배당을 더블형태로 변환한다.
	 * 
	 * @param rate
	 * @return
	 */
	protected Double convertRate(String rate) {
		if (rate == null || "".equals(rate)) {
			rate = "0";
		}
		return Double.parseDouble(rate);
	}

	/**
	 * 리그코드 변경
	 * 
	 * @param league
	 * @return
	 */
	protected String getLeagueCode(String league) {
		String leagueCode = leagueMap.get(league);
		if (leagueCode == null || "".equals(leagueCode)) {
			leagueCode = league;
			// log.error("{} {} 리그코드 변경에러, 리그명 = {}", SITE, ITEM_NAME, league);
		}
		return leagueCode;
	}

	/**
	 * 팀코드 변경
	 * 
	 * @param team
	 * @return
	 */
	protected String getTeamCode(String team) {
		String teamCode = teamMap.get(team);
		if (teamCode == null || "".equals(teamCode)) {
			teamCode = team;
			// log.error("{} {} 팀코드 변경에러, 팀명 = {}", SITE, ITEM_NAME, team);
		}
		return teamCode;
	}

	/**
	 * BET_TYPE, SUB_TYPE 에서 베팅타입을 가져온다.
	 * 
	 * @param key
	 * @return
	 */
	protected Map<String, String> getBetType(String data) {

		String[] datas = data.split(",");
		Map<String, String> map = new HashMap<String, String>();

		Pattern p = Pattern.compile("\\[" + datas[1] + ",'([^']+)'");
		Matcher m = p.matcher(BET_TYPE);

		if (!m.find())
			return map;

		map.put("betName", m.group(1));
		map.put("handicap", datas[4]);

		if (!"0".equals(datas[0])) { // 스페셜
			p = Pattern.compile("\\[" + datas[1] + ",'([^']+)'");
			m = p.matcher(SUB_TYPE);

			if (m.find()) {
				map.put("betName", m.group(1) + " - " + map.get("betName"));
				map.put("subType", m.group(1));
				map.put("subCode", datas[1]);
			}
		}

		return map;
	}

	/**
	 * 더블찬스
	 * 
	 * @param game
	 * @param map
	 */
	protected void betDoubleChance(Game game, Map<String, String> map) {
		String[] handi = map.get("handi").split(",");

		for (int i = 0; i < handi.length; i++) {
			Game betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType(map.get("betType"));
			betting.setBetName(map.get("betName"));
			betting.setHandicap(convertHandicap(map.get("handicap")));
			betting.setHandicapName("");
			betting.setBetRateName("");
			betting.setBetRate(convertRate(handi[i]));

			if (i == 0) { // 1X
				betting.setBetRateCode("1X");
			} else if (i == 1) { // 12
				betting.setBetRateCode("12");
			} else { // X2
				betting.setBetRateCode("X2");
			}
			game.addGameList(betting);
		}
	}

	/**
	 * 1X2 매치게임
	 * 
	 * @param game
	 * @param map
	 */
	protected void betMatch(Game game, Map<String, String> map) {
		String[] handi = map.get("handi").split(",");

		for (int i = 0; i < handi.length; i++) {
			Game betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType(map.get("betType"));
			betting.setBetName(map.get("betName"));
			betting.setHandicap(convertHandicap(map.get("handicap")));
			betting.setHandicapName("");
			betting.setBetRateName("");
			betting.setBetRate(convertRate(handi[i]));

			if (i == 0) { // 1
				betting.setBetRateCode("1");
			} else if (i == 1) { // X
				betting.setBetRateCode("X");
			} else { // 2
				betting.setBetRateCode("2");
			}
			game.addGameList(betting);
		}
	}

	/**
	 * 오버/언더 게임
	 * 
	 * @param game
	 * @param map
	 */
	protected void betOverUnder(Game game, Map<String, String> map) {
		String[] handi = map.get("handi").split(",");

		for (int i = 0; i < handi.length; i++) {
			Game betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType(map.get("betType"));
			betting.setBetName(map.get("betName"));
			betting.setHandicap(convertHandicap(map.get("handicap")));
			betting.setHandicapName("");
			betting.setBetRateName("");
			betting.setBetRate(convertRate(handi[i]));

			if (i == 0) { // Over
				betting.setBetRateCode("Over");
			} else { // Under
				betting.setBetRateCode("Under");
			}
			game.addGameList(betting);
		}
	}

	/**
	 * 핸디캡 게임
	 * 
	 * @param game
	 * @param map
	 */
	protected void betHandicap(Game game, Map<String, String> map) {
		String[] handi = map.get("handi").split(",");

		for (int i = 0; i < handi.length; i++) {
			Game betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType(map.get("betType"));
			betting.setBetName(map.get("betName"));
			betting.setHandicapName("");
			betting.setBetRate(convertRate(handi[i]));

			if (i == 0) { // 1
				betting.setHandicap(convertHandicap(map.get("handicap")));
				betting.setBetRateCode("1");
				betting.setBetRateName(game.getHomeTeamCode());
			} else { // 2
				betting.setHandicap(convertHandicap(map.get("handicap")) * -1);
				betting.setBetRateCode("2");
				betting.setBetRateName(game.getAwayTeamCode());
			}
			game.addGameList(betting);
		}
	}

	/**
	 * 디비 입력
	 * 
	 * @param game
	 * @return
	 */
	protected Integer addBetting(Game game) {

		sbobetDao.delete(game);
		return sbobetDao.addBetting(game);
	}

}
